//6.封装一个Watcher类
class Watcher {
  constructor(data, key) {
    //Watcher实例化的时候接受两个参数,一个是key所在的对象 一个是key
    this.data = data;
    this.key = key;
    //当获取值的时候,将当前的watcher保存在Dep的静态变量上，其实是为了全局共享这个Watcher实例，,然后我们在defineReactive的getter函数中可以收集到这个watcher
    Dep.target = this;
    this.get();
    Dep.target = null;
  }
  //当组件内new Watcher的时候就是想得到一个值,所以封装一个get方法拿值
  get() {
    return this.data[this.key];
  }
  update() {
    console.log(
      "我被通知了，重新获取数据：" + this.get() + "开始重新渲染视图"
    );
  }
}

//5.定义一个Dep类,用来收集依赖 和 通知更新
class Dep {
  constructor() {
    //每次实例化Dep就会创建一个空的数组,然后用来保存所有的watcher
    this.subs = [];
  }
  //收集依赖的函数，将watcher放进subs数组收集起来
  depend(watcher) {
    this.subs.push(watcher);
    console.log("每次访问我的数据的时候,我就把watcher收集到dep.subs数组中");
  }
  //更新通知的函数
  notify() {
    console.log("通知所有的watcher 重新加载新的数据");
    this.subs.forEach((watcher) => {
      watcher.update();
    });
  }
}

//4.封装defineReactive函数，主要是为了对单个数据做数据劫持（数据劫持的核心代码）
function defineReactive(data, key, value) {
  const dep = new Dep();
  //将数据传入observe深度监听
  observe(value);

  Object.defineProperty(data, key, {
    get: function reactiveGetter() {
      //当这个数据被请求的时候,把观察者watcher收集起来到dep.subs中
      console.log("正在获取" + key + "的值", "收集所有依赖项watcher");
      //并不是所有时候我们都是通过模板解析在获取数据，有可能是通过方法或者取值，所以我们需要做判断，判断这个存储watcher的全局变量是否为有新的watcher
      if (Dep.target) {
        dep.depend(Dep.target);
      }
      return value;
    },
    set: function reactiveSetter(newVal) {
      //新的值与旧的值一样，则不做更改直接 return
      if (newVal === value) return;
      console.log("正在设置" + key + "的值", "通知所有的watcher进行更新");
      value = newVal;
      //当我们的响应式数据被修改更新的时候，通知所有dep.subs里的watcher进行更新
      dep.notify();
    },
  });
}

//3.封装一个Observer类，主要用来处理发布订阅模式的核心操作（响应式操作：收集依赖，通知更新）
class Observer {
  constructor(data) {
    this.data = data;
    if (Array.isArray(data)) {
      this.observeArray(data);
    } else {
      this.walk(data);
    }
  }
  //如果是数组就将内部的数据设置响应式
  observeArray(data) {
    data.forEach((item) => {
      observe(item);
    });
  }
  //如果是对象，将需要的数据，属性名，属性值 给到响应式核心函数
  walk(data) {
    for (let key of Object.keys(data)) {
      defineReactive(data, key, data[key]);
    }
  }
}

//2.对数据开始响应式的操作，将数据传入观察者函数
function observe(data) {
  if (typeof data !== "object" || data === null) return;
  new Observer(data);
}

//1.封装一个Vue的构造函数
function Vue(options) {
  //把配置对象内的data在_data里放一份
  this._data = options.data;
  //【进行数据代理】,把_data中的属性取出来,一个个的放在vue实例上
  for (let key of Object.keys(this._data)) {
    Object.defineProperty(this, key, {
      get: function proxyGetter() {
        return this._data[key];
      },
      set: function proxySetter(newVal) {
        this._data[key] = newVal;
      },
    });
  }

  //拿到_data的数据开始进行响应式操作，我们首先把响应式操作的代码放在observe函数中
  observe(this._data);
}

const vm = new Vue({
  data: {
    name: "lingHua",
    age: 18,
    movieList: [
      {
        id: "001",
        movieName: "肖申克的救赎",
      },
      {
        id: "002",
        movieName: "盗梦空间",
      },
    ],
    weather: {
      type: "晴天",
      temperature: 28,
    },
  },
});

//创建组件测试函数
function createComponent() {
  new Watcher(vm, "movieList");
}

//修改数据的测试函数
function updateData() {
  // vm.name = "凌华";
  vm.movieList=[{
    id: "001",
    movieName: "朝花夕誓",
  },
  {
    id: "002",
    movieName: "游戏人生Zero",
  },]

}
// 模拟创建组件和使用插值语法
createComponent()
//模拟修改数据然后更新视图，模板函数没有写
updateData()
